#include "obuinfo.h"
#include "func/lanefunction.h"
#include "func/vehplateutils.h"
#include <QtEndian>
#include <QTextStream>
#include <QDebug>

ObuInfo::ObuInfo(QObject *parent) :
    QObject(parent)
{
    memset(&m_rawSysInfoFile, 0, sizeof m_rawSysInfoFile);
    memset(&m_rawCfg, 0, sizeof m_rawCfg);
}

bool ObuInfo::setObuInfo(const quint8 *sysInfo, quint8 sizeOfSysInfo, const quint8 *obuCfg)
{
    if ((sizeOfSysInfo<MIN_SYSFILE_SIZE) ||
        (sizeOfSysInfo>sizeof(m_rawSysInfoFile)))
    {
        logger()->info(tr("Invalid sizeOfSysInfo: [%1]").arg(sizeOfSysInfo));
        return false;
    }
    RawObuSysInfoFile rawSys;
    memset(&rawSys, 0, sizeof rawSys);
    memcpy(&rawSys, sysInfo, sizeOfSysInfo);
    QDate d1, d2;
    if (!bcd2date(&d1, rawSys.contractSignedDate))
    {
        logger()->info(tr("Invalid contractSignedDate"));
        return false;
    }
    if (!bcd2date(&d2, rawSys.contractExpiredDate))
    {
        logger()->info(tr("Invalid contractExpiredDate"));
        return false;
    }
    this->m_contractSignedDate=d1;
    this->m_contractExpiredDate=d2;
    memcpy(&this->m_rawSysInfoFile, &rawSys, sizeof rawSys);
    memcpy(&this->m_rawCfg, obuCfg, OBU_CFG_SIZE);

    // 对山东发行的OBU，解析其系统文件内的车牌和车牌颜色
    // "\xc9\xbd\xb6\xab": "山东"的GB2312编码
    if (0==memcmp("\xc9\xbd\xb6\xab", rawSys.contractProvider, 4))
    {
        setVehPlateNumber(rawSys.vehicleLicencePlateNumber, sizeof(rawSys.vehicleLicencePlateNumber));
        setVehPlateColor(rawSys.vehicleLicencePlateColor[0], rawSys.vehicleLicencePlateColor[1]);
    }
    return true;
}

void ObuInfo::setVehPlateNumber(quint8 *pPlate, int nLen)
{
    // 车牌转换为UTF-8
    QByteArray ba((char *)pPlate, nLen);
    // 正常车牌以\x00结束
    ba.replace('\x00', ' ');
    // 有的测试OBU没有在系统信息文件中写车牌，该部分填充全\xFF，已经查询过GB2312编码没有\xFF
    ba.replace('\xFF', ' ');
    m_vehicleLicencePlateNumber=QTextCodec::codecForName("GB2312")->toUnicode(ba).trimmed();
}

void ObuInfo::setVehPlateColor(quint8 bHi, quint8 bLo)
{
    // 车牌颜色，确实存在车牌相同、车牌颜色不同的车辆，所以必须要存储车牌颜色
    if ((0xFF==bHi) ||
        (0xFF==bLo))
    {
        // 有的OBU没有写车牌颜色
        m_vehicleLicencePlateColor=0;
    }
    else
    {
        m_vehicleLicencePlateColor=bLo;
    }
}

bool ObuInfo::setObuInfo(const QString &sysInfo, const QString &obuCfg)
{
    if (sysInfo.length()>MAX_SYSFILE_SIZE*2)
    {
        return false;
    }
    if (obuCfg.length()!=OBU_CFG_SIZE*2)
    {
        return false;
    }
    quint8 rawSys[MAX_SYSFILE_SIZE];
    quint8 rawCfg[OBU_CFG_SIZE];
    if (!hex2Raw(rawSys, sysInfo.toStdString().c_str(), sysInfo.length()))
    {
        return false;
    }
    if (!hex2Raw(rawCfg, obuCfg.toStdString().c_str(), obuCfg.length()))
    {
        return false;
    }
    return setObuInfo(rawSys, sysInfo.length()/2, rawCfg);
}

bool ObuInfo::setCipheredVehInfoFile(const quint8 *vehInfoFile, quint8 fileLen)
{
    memcpy(this->m_cipheredVehInfo, vehInfoFile, fileLen);
    return true;
}

bool ObuInfo::setDecipheredVehInfoFile(const quint8 *vehInfoFile, quint8 fileLen)
{
    memset(m_decipheredVehInfo, 0, sizeof m_decipheredVehInfo);
    memcpy(m_decipheredVehInfo, vehInfoFile+9, qMin(fileLen-9, (int)sizeof(m_decipheredVehInfo)));
    setVehPlateNumber(m_decipheredVehInfo, 12);
    setVehPlateColor(m_decipheredVehInfo[12], m_decipheredVehInfo[13]);
    return true;
}

quint8 *ObuInfo::getDeliveryKeyCmdForEncrypt() const
{
    //命令报文规范
    //CLA: 80, INS: 1A, P1: 密钥用途, P2: 密钥标识, Lc: 分散数据长度=分散级数*8
    //Data: OBU合同序号(8 bytes) || 省份标识(8 bytes，如"山东山东")
    static quint8 cmd[32];
    cmd[0]=21;              // length of cmd
    cmd[1]=0x80;            // CLA
    cmd[2]=0x1A;            // INS
    cmd[3]=0x59;            // P1 - OBU加密主密钥，参收费技术要求2.3.2 DF01下的密钥文件结构
    cmd[4]=0x03;            // P2 - OBU加密主密钥，参收费技术要求2.3.2 DF01下的密钥文件结构
    cmd[5]=0x10;            // Lc
    memcpy(cmd+6, this->m_rawSysInfoFile.contractSerialNumber, 8);
    memcpy(cmd+14, this->m_rawSysInfoFile.contractProvider, 4);
    memcpy(cmd+18, this->m_rawSysInfoFile.contractProvider, 4);
    return cmd;
}

quint8 *ObuInfo::getDecipherDataCmd() const
{
    //命令报文规范
    //CLA: 80, INS: FA
    //P1: 00-加密计算; 05-唯一一块MAC计算; 08-交通运输部MAC计算; 80-无后续块解密
    //P2: 00
    //Lc: P1=08时，Lc>=9; P2<>08时，如使用DES算法，Lc=n*8
    //Data: 安全计算数据
    static quint8 cmd[256];
    cmd[0]=29;              // length of cmd
    cmd[1]=0x80;            // CLA
    cmd[2]=0xFA;            // INS
    cmd[3]=0x80;            // P1
    cmd[4]=0x00;            // P2
    cmd[5]=0x18;            // Lc
    memcpy(cmd+6, this->m_cipheredVehInfo, 0x18);
    return cmd;
}

quint32 ObuInfo::getMacId() const
{
    return qFromBigEndian(this->m_rawCfg.macID);
}

bool ObuInfo::getTampered() const
{
    return testBit(this->m_rawCfg.obuStatus, 1);
}

bool ObuInfo::getLocked() const
{
    return !testBit(this->m_rawCfg.obuStatus, 2);
}

bool ObuInfo::hasCpuCard() const
{
    bool iccPresent=!testBit(this->m_rawCfg.obuStatus, 7);
    bool iccIsCpuCard=!testBit(this->m_rawCfg.obuStatus, 4);
    bool iccStatus=!testBit(this->m_rawCfg.obuStatus, 3);
    return iccPresent && iccIsCpuCard && iccStatus;
}

bool ObuInfo::isFromShandong() const
{
    return 0==memcmp("\xc9\xbd\xb6\xab", this->m_rawSysInfoFile.contractProvider, 4);
}

QString ObuInfo::getContractSN() const
{
    char szSN[20];
    raw2HexStr(szSN, this->m_rawSysInfoFile.contractSerialNumber, sizeof this->m_rawSysInfoFile.contractSerialNumber);
    return szSN;
}

QDate ObuInfo::getSignedDate() const
{
    return this->m_contractSignedDate;
}

QDate ObuInfo::getExpiredDate() const
{
    return this->m_contractExpiredDate;
}

QString ObuInfo::getVehicleLicencePlateNumber() const
{
    return this->m_vehicleLicencePlateNumber;
}

QString ObuInfo::getVehicleLicencePlateColorAndNumber() const
{
    return VehPlateUtils::getVehicleLicencePlateColorAndNumber(
        getVehicleLicencePlateColor(),
        getVehicleLicencePlateNumber());
}

quint16 ObuInfo::getVehicleLicencePlateColor() const
{
    return this->m_vehicleLicencePlateColor;
}
